package com.golxen.framework.db;


import com.alibaba.druid.util.JdbcUtils;
import com.golxen.framework.common.BuiltinPlugin;
import com.golxen.framework.common.server.eServerType;
import com.golxen.framework.common.server.game.dao.CrossServerInfoDaoImpl;
import com.golxen.framework.common.server.game.dao.DbInfoDaoImpl;
import com.golxen.framework.common.server.game.dao.entity.CrossServerInfo;
import com.golxen.framework.common.server.game.dao.entity.DbInfo;
import com.golxen.utils.LogUtil;
import com.golxen.utils.YmlUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

//数据库连接的设置和获取使用，批处理，要先设置需要连接的数据库connName
public class DBManager {
    private Connection myConn;
    private static DBPoolManager pools;

    private String connName;

    long openConnectionBeforeTime;

    static ConcurrentHashMap<String, AtomicLong> connectionCounts = new ConcurrentHashMap<String, AtomicLong>();

    public static String serverDbName;
    public static String userDbName;
    public static String logDbName;
    public static String configDbName;
    public static String crossDbName;

    static boolean initGameServer;
    static boolean initCrossServer;

    public DBManager() {
    }

    public static boolean initConfig() {
        if (initGameServer) {
            return true;
        }
        serverDbName=YmlUtil.getString("dbName.server");
        userDbName=YmlUtil.getString("dbName.user");
        logDbName=YmlUtil.getString("dbName.log");
        configDbName=YmlUtil.getString("dbName.config");
        crossDbName=YmlUtil.getString("dbName.cross");

        pools = DBPoolManager.getInstance();
        initGameServer = pools != null;
        return initGameServer;
    }


    public static boolean initCrossDbConfig(eServerType serverType) {

        if (serverType == eServerType.CROSS && !initCrossServer) {
            initCrossServer = true;
            // 跨服
            int channelId = BuiltinPlugin.getChannelId();
            int serverId = 0;
            serverId = BuiltinPlugin.getCrossServerId();
            CrossServerInfo crossServerInfo = new CrossServerInfoDaoImpl().getCrossServerInfo(channelId, serverId);
            List<DbInfo> infos = crossServerInfo.getDbInfos();
            if (addServerDbPool(serverType, infos)) {
                return true;
            }
            return false;
        }

//        if (serverType == eServerType.CROSS2 && !initCross2Server) {
//            initCross2Server = true;
//            // 跨服
//            int channelId = ConfigHelper.getInt("channelId");
//            int serverId = 0;
//            if (serverType == eServerType.CROSS2) {
//                serverId = BuiltinPlugin.getCross2ServerId();
//            }
//            CrossServerInfo crossServerInfo = new CrossServerInfoDaoImpl().getCrossServerInfo(channelId, serverId);
//            List<DbInfo> infos = crossServerInfo.getDbInfos();
//            if (addServerDbPool(serverType, infos)) {
//                return true;
//            }
//            return false;
//        }

        return true;
    }

    private static boolean addServerDbPool(eServerType serverType, List<DbInfo> infos) {
        if (infos == null || infos.size() == 0) {
            return false;
        }

        for (DbInfo info : infos) {

            String dbInfoValueHead = "jdbc:mysql://" + info.getIp() + ":" + info.getPort() + "/";
            String dbInfoValueTail = "?useUnicode=true&characterEncoding=utf8&useSSL=false";

            String url;

            url=dbInfoValueHead + info.getDbName() + dbInfoValueTail;

            pools.addPools(url,info.getUserName(),info.getPassword());

            LogUtil.CONSOLE.info("[DB] add pool {} {} {}", serverType, info.getDbName(), dbInfoValueHead);
        }

        return true;
    }

    public void setConnName(String connName) {
        this.connName = connName;
    }

    public static boolean initGameDbConfig(eServerType serverType) {
        if (serverType == eServerType.CROSS || serverType == eServerType.CROSS2) {
            return initCrossDbConfig(serverType);
        }

        // GameServer有查cross的db逻辑
        if (serverType == eServerType.GAME && !initCrossServer) {
            boolean initCrossDbConfig = initCrossDbConfig(eServerType.CROSS);
            if (!initCrossDbConfig) {
                return false;
            }
        }

        DbInfoDaoImpl daoImpl = new DbInfoDaoImpl();
        List<DbInfo> infos = daoImpl.getServerDbInfo(BuiltinPlugin.getServerId());
        if (addServerDbPool(serverType, infos)) {
            return true;
        }
        return false;
    }



    /**
     * 释放数据库相关资源
     */
    public static void stop() {
        pools.closeFallow();
        pools = null;
    }

    //当前数据库的连接次数
    AtomicLong getCount() {
        AtomicLong atomicLong = connectionCounts.get(connName);
        if (atomicLong == null) {
            atomicLong = new AtomicLong();
            AtomicLong putIfAbsent = connectionCounts.putIfAbsent(connName, atomicLong);
            if (putIfAbsent != null) {
                atomicLong = putIfAbsent;
            }
        }
        return atomicLong;
    }

    //根据数据库名coonName从连接池获取连接
    private boolean openConnection() {
        openConnectionBeforeTime = System.currentTimeMillis();
        Exception exception = null;
        try {

            if ((connName == null) || (connName.equals(""))) {
                setConnName(userDbName);
            }

            // return false;
            if (myConn != null && !myConn.isClosed()) {
                throw new RuntimeException("repeat Open Connection!");
            }

            myConn = pools.getConnection(connName);
            if (myConn != null) {
                getCount().incrementAndGet();
                return true;
            }
        } catch (Exception e) {
            exception = e;

            JdbcUtils.close(myConn);
            myConn = null;
        } finally {
            long openConnectionAfterTime = System.currentTimeMillis();
            long time = openConnectionAfterTime - openConnectionBeforeTime;
            if (exception != null) {
                LogUtil.DB.error("open {} connection time {}ms", this.connName, time, exception);
            } else if (time > 5) {
                LogUtil.DB.warn("open {} connection time {}ms", this.connName, time);
            } else {
                LogUtil.DB.debug("open {} connection time {}ms", this.connName, time);
            }
        }

        return false;
    }


    private void closeConnection(PreparedStatement pstmt) {
        try {
            if (pstmt == null) {
                return;
            }

            if (pstmt.isClosed() == false) {
                pstmt.clearParameters();
            }
            if (myConn != null && myConn.isClosed() == false) {
                if (myConn.getAutoCommit() != true) {
                    myConn.commit();
                    myConn.setAutoCommit(true);
                }
            }
        } catch (SQLException e) {
            LogUtil.DB.error(e.getMessage(), e);
        } finally {
            JdbcUtils.close(pstmt);
            JdbcUtils.close(myConn);
            myConn = null;

        }
    }


    public void closeConnection(PreparedStatement pstmt, ResultSet rs) {
        JdbcUtils.close(rs);
        closeConnection(pstmt);
    }

    /**
     * 提供Connection接口，用于特殊处理（如事务处理）； 调用该接口后请使用closeConnection关闭连接；
     */
    public Connection getConnection() {
        if (!openConnection())
            return null;
        return myConn;
    }

    /**
     * 批处理操作
     *
     * @param sqlText
     * @param params
     * @return
     */
    public int executeBatchQuery(String sqlText, List<Map<Integer, Object>> params) {
        if (params == null || !openConnection())
            return -1;
        int count = 0;

        PreparedStatement pstmt = null;

        try {
            myConn.setAutoCommit(false);
            //ResultSet.TYPE_SCROLL_SENSITIVE 允许结果集(ResultSet)的游标可以上下移动（读取后不清内存）
            pstmt = myConn.prepareStatement(sqlText, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
            for (Map<Integer, Object> map : params) {
                prepareCommand(pstmt, map);
                pstmt.addBatch();
                count++;
                if (count % 1500 == 0) {
                    pstmt.executeBatch();
                    myConn.commit();
                    count = 0;
                }
            }
            //还有没commit的
            if (count > 0) {
                pstmt.executeBatch();
                myConn.commit();
            }

            return 1;
        } catch (Exception ex) {
            LogUtil.DB.error(ex.getMessage(), ex);
        } finally {

            try {
                myConn.setAutoCommit(true);
            } catch (SQLException e) {
                LogUtil.DB.error(e.getMessage(), e);
            }
            closeConnection(pstmt);

        }
        return -1;
    }

    private void prepareCommand(PreparedStatement pstmt, Map<Integer, Object> parms) throws SQLException {
        if (parms == null)
            return;
        for (Map.Entry<Integer, Object> entry : parms.entrySet()) {
            pstmt.setObject(entry.getKey(), entry.getValue());
        }
    }

}
